package com.zonta.finance.serivce.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zonta.finance.common.constant.ApiResponseCode;
import com.zonta.finance.common.dto.TokenResponse;
import com.zonta.finance.common.exception.BizException;
import com.zonta.finance.common.exception.LoginException;
import com.zonta.finance.common.exception.ParameterException;
import com.zonta.finance.common.service.TokenService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.zonta.finance.common.utils.DateUtil;
import com.zonta.finance.common.utils.MyUtil;
import com.zonta.finance.domain.po.Member;
import com.zonta.finance.domain.po.MemberBindPhone;
import com.zonta.finance.enums.SmsCodeTypeEnum;
import com.zonta.finance.serivce.IMemberBindPhoneService;
import com.zonta.finance.serivce.IMemberService;
import com.zonta.finance.serivce.MemberLoginService;
import com.zonta.finance.constant.RedisKeyConstant;
import com.zonta.finance.domain.dto.AdminDTO;
import com.zonta.finance.domain.form.GetBaseCodeForm;
import com.zonta.finance.domain.form.GetSmsCodeForm;
import com.zonta.finance.domain.dto.SmsCodeResult;
import com.zonta.finance.domain.form.PhonePasswordLoginForm;
import com.zonta.finance.domain.form.PhoneSmsCodeLoginForm;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class MemberLoginServiceImpl implements MemberLoginService {
    private final RedisTemplate<String, Object> redisTemplate;
    private final IMemberBindPhoneService memberBindPhoneService;
    private final PasswordEncoder passwordEncoder;
    private final IMemberService memberService;
    private final ObjectMapper objectMapper;
    private final TokenService<AdminDTO> adminDTOTokenService;
    /**
     * 获取客户端id
     * @return
     */
    @Override
    public String getClientId() {
        return UUID.randomUUID().toString().replace("-",""); //获取uuid 把-去掉
    }

    /**
     * 获取图形验证码
     * @param form 请求参数
     * @return
     */
    @Override
    public String getBase64Code(GetBaseCodeForm form) {
        /**
         * 用糊涂工具包 获取base验证码 1000代表 验证码干扰度
         */
                LineCaptcha captcha = CaptchaUtil.createLineCaptcha(300, 192, 5, 1000);
        //获取客户端id 用于和验证码存入redis
        String clientId = form.getClientId();
        log.info("客户端id:{},图形验证码:{}",clientId,captcha.getCode());
        // 客户端id和验证码存入redis 15分钟后过期
        redisTemplate.opsForValue().set(RedisKeyConstant.GRAPHIC_VERIFICATION_CODE+clientId,captcha.getCode(),15, TimeUnit.MINUTES);
        return captcha.getImageBase64();
    }

    @Override
    public void sendSmsCode(GetSmsCodeForm form) {
        //校验图形验证码是否合法
        checkBase64Code(form.getClientId(),form.getCode());
        //用于存入redis key
        String key= RedisKeyConstant.SMS_CODE+form.getSmsCodeType()+form.getPhone();
        // 通过key获取用户近期是否获取短信
        SmsCodeResult smsCodeResult = (SmsCodeResult) redisTemplate.opsForValue().get(key);
        if (smsCodeResult!=null) {
            //时间对比。在redis的时间和现在的时间相差小于60秒
            Duration duration = DateUtil.getDuration(smsCodeResult.getGetTime(), DateUtil.getSystemTime());
            if (duration.getSeconds() < 60) {
                throw new LoginException("验证码获取太频繁了,请稍后重试");
            }
        }
        //初始化手机号
        String memberBindPhone="";
        //通过手机号查询用户信息
        MemberBindPhone menber = memberBindPhoneService.getOne(new LambdaQueryWrapper<MemberBindPhone>().eq(MemberBindPhone::getPhone, form.getPhone()));
        // 判断是否查到数据
        if (ObjUtil.isNotEmpty(menber)){
            memberBindPhone = menber.getPhone();
        }
        // 判断是否注册
        if ( StrUtil.isNotEmpty(memberBindPhone) && form.getSmsCodeType().equals(SmsCodeTypeEnum.REGISTER.getCode()) ){
            throw new ParameterException("phone","该手机号已注册");
        }
        if (StrUtil.isEmpty(memberBindPhone) &&  form.getSmsCodeType().equals(SmsCodeTypeEnum.LOGIN.getCode()) ){
            throw new ParameterException("phone","该手机号未注册");
        }
        // 如果上面条件都没有满足，下则是发送短信验证码
        //设置6位随机验证码并存入redis 15分钟过期
        int smsCode = MyUtil.getRandom(6);
        SmsCodeResult codeResult = new SmsCodeResult();
        codeResult.setCode(String.valueOf(smsCode));
        codeResult.setGetTime(DateUtil.getSystemTime());
        redisTemplate.opsForValue().set(key,codeResult,15,TimeUnit.MINUTES);
        log.info("客户端id{},手机号：{},短信验证码：{}", form.getClientId(), form.getPhone(), smsCode);
        // todo 发送短信验证码
    }

    /**
     * 校验图形验证码是否正确
     * @param clientId 客户端id
     * @param code 输入的验证码
     * @return
     */
    @Override
    public boolean checkBase64Code(String clientId,String code){
        String cacheCode = (String) redisTemplate.opsForValue().get(RedisKeyConstant.GRAPHIC_VERIFICATION_CODE + clientId);

        if (StrUtil.isEmpty(cacheCode)||!cacheCode.equalsIgnoreCase(code)){
            throw new LoginException("验证码错误");
        }
        redisTemplate.delete(RedisKeyConstant.GRAPHIC_VERIFICATION_CODE+clientId);
        return true;
    }

    /**
     * 校验
     * @param phone 手机号
     * @param smsCode 短信验证码
     * @param smsCodeType 类型
     * @return
     */
    @Override
    public boolean checkSmsCode(String phone, String smsCode, String smsCodeType) {
        SmsCodeResult smsCodeResult = (SmsCodeResult) redisTemplate.opsForValue().get(RedisKeyConstant.SMS_CODE + smsCodeType + phone);
        redisTemplate.delete(RedisKeyConstant.SMS_CODE + smsCodeType + phone);
        if (ObjUtil.isEmpty(smsCodeResult) || !smsCodeResult.getCode().equals(smsCode)){
            throw new ParameterException("smsCode", "短信证码错误，请重新获取验证码！");
        }
        return true;
    }

    /**
     * 手机密码登录
     * @param form 客户端id 手机号 密码 图形验证码
     * @return
     */
    @Override
    public TokenResponse phonePasswordLogin(PhonePasswordLoginForm form) {
        checkBase64Code(form.getClientId(),form.getCode());
        MemberBindPhone memberBindPhone = memberBindPhoneService.getOne(new LambdaQueryWrapper<MemberBindPhone>().eq(MemberBindPhone::getPhone, form.getPhone()));
        if (ObjUtil.isEmpty(memberBindPhone) || StringUtil.isEmpty(memberBindPhone.getPassword())){
            throw new BizException(ApiResponseCode.ACCOUNT_PASSWORD_ERROR.getCode(),
                    ApiResponseCode.ACCOUNT_PASSWORD_ERROR.getMessage());
        }
        if (!passwordEncoder.matches(form.getPassword(),memberBindPhone.getPassword())){
            throw new BizException(ApiResponseCode.ACCOUNT_PASSWORD_ERROR.getCode(),
                    ApiResponseCode.ACCOUNT_PASSWORD_ERROR.getMessage());
        }
        Member member = memberService.getOne(new LambdaQueryWrapper<Member>().eq(Member::getId, memberBindPhone.getMemberId()));

        return loginSuccess(member,form.getClientId());
    }

    /**
     * 将设置
     * @return
     */
    public TokenResponse loginSuccess(Member member,String clientId){
        try {
            AdminDTO adminDTO = new AdminDTO();
            adminDTO.setId(member.getId());
            adminDTO.setTenantId(member.getTenantId());
            adminDTO.setSysRoleIds(objectMapper.readValue(member.getSysRoleIds(), new TypeReference<Set<Long>>() {
            }));
            adminDTOTokenService.setToken(adminDTO);
            return adminDTO.getToken();
        }catch (Exception e){
            e.printStackTrace();
            throw new LoginException("登录失败!");
        }


    }

    /**
     * 手机短信登录
     * @param form  客户端id 手机号  图形验证码
     * @return
     */
    @Override
    public TokenResponse phoneSmsCodeLogin(PhoneSmsCodeLoginForm form) {
        checkSmsCode(form.getPhone(),form.getSmsCode(),SmsCodeTypeEnum.LOGIN.getCode());
        MemberBindPhone one = memberBindPhoneService.getOne(new LambdaQueryWrapper<MemberBindPhone>().eq(MemberBindPhone::getPhone, form.getPhone()));
        if (ObjUtil.isEmpty(one)){
            throw new LoginException("该手机号未注册");
        }
        Member member = memberService.getById(one.getMemberId());
        return loginSuccess(member, form.getClientId());
    }

    /**
     * 获取客户端token
     * @param clientId 客户端id
     * @return
     */
    @Override
    public TokenResponse getClientToken(String clientId) {
        return (TokenResponse) redisTemplate.opsForValue().get(RedisKeyConstant.CLIENT_TOKEN_KEY + clientId);
    }
}
